package com.skivingcloud.report.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.mybatisflex.core.mask.MaskManager;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.skivingcloud.admin.conf.CommonPropertiesConfig;
import com.skivingcloud.admin.utils.UserUtil;
import com.skivingcloud.common.utils.JacksonUtil;
import com.skivingcloud.report.config.DataSourceConfig;
import com.skivingcloud.report.constants.ReportConstant;
import com.skivingcloud.report.entity.MesExecSqlDto;
import com.skivingcloud.report.entity.ReportDatasource;
import com.skivingcloud.report.mapper.ReportDatasourceMapper;
import com.skivingcloud.report.service.JdbcService;
import com.skivingcloud.report.service.ReportDatasourceService;
import com.skivingcloud.report.utils.InfluxDBConnection;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.skivingcloud.common.utils.PageParameter;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 报表数据源 服务层实现。
 *
 * @author skivingcloud
 * @since 2024-11-07
 */
@Service
public class ReportDatasourceServiceImpl extends ServiceImpl<ReportDatasourceMapper, ReportDatasource> implements ReportDatasourceService {
    private static final Logger log = LoggerFactory.getLogger(ReportDatasourceServiceImpl.class);

    private final RestTemplate restTemplateOuter;
    private final JdbcService jdbcService;
    private final CommonPropertiesConfig commonPropertiesConfig;

    public ReportDatasourceServiceImpl(@Qualifier("restTemplateOuter") RestTemplate restTemplateOuter, JdbcService jdbcService, CommonPropertiesConfig commonPropertiesConfig) {
        this.restTemplateOuter = restTemplateOuter;
        this.jdbcService = jdbcService;
        this.commonPropertiesConfig = commonPropertiesConfig;
    }

    /**
     * 分页查询报表数据源
     *
     * @param parameter 查询参数
     * @return 分页查询结果
     */
    @Override
    public Page<ReportDatasource> queryByPage(PageParameter<ReportDatasource> parameter){
        Page<ReportDatasource> page = new Page<>(parameter.getPage(), parameter.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (parameter.getParam() != null) {
            //查询条件
            if (StringUtils.isNoneBlank(parameter.getParam().getId())) {
                queryWrapper.and(ReportDatasource::getId).like(parameter.getParam().getId());
            }
            if (commonPropertiesConfig.getEnabledTenant()) {
                queryWrapper.and(ReportDatasource::getTenantId).eq(UserUtil.getCurrentUser().getTenantId());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getCode())) {
                queryWrapper.and(ReportDatasource::getCode).like(parameter.getParam().getCode());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getName())) {
                queryWrapper.and(ReportDatasource::getName).like(parameter.getParam().getName());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getType())) {
                queryWrapper.and(ReportDatasource::getType).eq(parameter.getParam().getType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDriverClass())) {
                queryWrapper.and(ReportDatasource::getDriverClass).like(parameter.getParam().getDriverClass());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getJdbcUrl())) {
                queryWrapper.and(ReportDatasource::getJdbcUrl).like(parameter.getParam().getJdbcUrl());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getUserName())) {
                queryWrapper.and(ReportDatasource::getUserName).like(parameter.getParam().getUserName());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getPassword())) {
                queryWrapper.and(ReportDatasource::getPassword).like(parameter.getParam().getPassword());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getApiColumnsPrefix())) {
                queryWrapper.and(ReportDatasource::getApiColumnsPrefix).like(parameter.getParam().getApiColumnsPrefix());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getApiColumns())) {
                queryWrapper.and(ReportDatasource::getApiColumns).like(parameter.getParam().getApiColumns());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getApiResultType())) {
                queryWrapper.and(ReportDatasource::getApiResultType).like(parameter.getParam().getApiResultType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getApiRequestType())) {
                queryWrapper.and(ReportDatasource::getApiRequestType).like(parameter.getParam().getApiRequestType());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getApiRequestHeader())) {
                queryWrapper.and(ReportDatasource::getApiRequestHeader).like(parameter.getParam().getApiRequestHeader());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getCreator())) {
                queryWrapper.and(ReportDatasource::getCreator).like(parameter.getParam().getCreator());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getUpdater())) {
                queryWrapper.and(ReportDatasource::getUpdater).like(parameter.getParam().getUpdater());
            }
            if (StringUtils.isNoneBlank(parameter.getParam().getDelFlag())) {
                queryWrapper.and(ReportDatasource::getDelFlag).like(parameter.getParam().getDelFlag());
            }
        }
        return this.page(page, queryWrapper);
    }

    /**
     * 测试数据源连接
     *
     * @param reportDatasource 报表数据源
     * @return 测试结果
     */
    @Override
    public Boolean testConnection(ReportDatasource reportDatasource) throws JsonProcessingException {
        ReportDatasource o = this.getById(reportDatasource.getId());
        if (o != null && o.getPassword().equals(reportDatasource.getPassword())) {
            reportDatasource.setPassword(this.getByIdWithoutMask(reportDatasource.getId()).getPassword());
        }
        switch (reportDatasource.getType()) {
            case ReportConstant.HTTP -> {
                return testHttp(reportDatasource);
            }
            case ReportConstant.INFLUXDB ->{
                return jdbcService.influxdbTest(reportDatasource.getUserName(), reportDatasource.getPassword(),
                        reportDatasource.getJdbcUrl().substring(0,reportDatasource.getJdbcUrl().lastIndexOf("/")),
                        null, null);
            }
            case ReportConstant.ELASTIC_SEARCH_SQL -> {
                return jdbcService.esTest(reportDatasource.getJdbcUrl(), reportDatasource.getUserName(), reportDatasource.getPassword());
            }
            case ReportConstant.MYSQL,
                 ReportConstant.KUDU_IMAPLA,
                 ReportConstant.ORACLE,
                 ReportConstant.SQL_SERVER,
                 ReportConstant.JDBC,
                 ReportConstant.POSTGRESQL,
                 ReportConstant.DAMENG,
                 ReportConstant.OPENGAUSS,
                 ReportConstant.KINGBASE,
                 ReportConstant.TDENGINE -> {
                DataSourceConfig dataSourceConfig = new DataSourceConfig(null, reportDatasource.getType(),
                        reportDatasource.getJdbcUrl(), reportDatasource.getUserName(),
                        reportDatasource.getPassword(), null);
                return jdbcService.testRelationalDb(dataSourceConfig);
            }
        }
        return true;
    }

    /**
     * 新增数据源
     *
     * @param reportDatasource 数据源
     * @return 结果
     */
    @Override
    public Boolean saveMe(ReportDatasource reportDatasource) {
        reportDatasource.setTenantId(UserUtil.getCurrentUser().getTenantId());
        reportDatasource.setDriverClass(ReportConstant.DRIVER_CLASS_MAP.get(reportDatasource.getType()));
        reportDatasource.setDelFlag(ReportConstant.COMMON_YES_OR_NO_NO);
        reportDatasource.setCreator(UserUtil.getCurrentUser().getId());
        reportDatasource.setCreateTime(LocalDateTime.now());
        return this.save(reportDatasource);
    }

    /**
     * 修改数据源
     *
     * @param reportDatasource 数据源
     * @return 结果
     */
    @Override
    public Boolean updateByIdMe(ReportDatasource reportDatasource) {
        ReportDatasource o = this.getById(reportDatasource.getId());
        reportDatasource.setDriverClass(ReportConstant.DRIVER_CLASS_MAP.get(reportDatasource.getType()));
        reportDatasource.setDelFlag(ReportConstant.COMMON_YES_OR_NO_NO);
        if(o.getPassword().equals(reportDatasource.getPassword())){
            reportDatasource.setPassword(null);
        }
        reportDatasource.setUpdater(UserUtil.getCurrentUser().getId());
        reportDatasource.setUpdateTime(LocalDateTime.now());
        return this.updateById(reportDatasource, true);
    }

    /**
     * 获取不脱敏的数据
     *
     * @param id 主键
     * @return 数据源
     */
    @Override
    public ReportDatasource getByIdWithoutMask(String id) {
        try {
            MaskManager.skipMask();
            //此处查询到的数据不会进行脱敏处理
            return this.getById(id);
        } finally {
            MaskManager.restoreMask();
        }
    }

    /**
     * 获取所有数据源
     *
     * @return 数据源
     */
    @Override
    public List<ReportDatasource> getAllReportDatasource() {
        QueryWrapper queryWrapper = QueryWrapper.create().where(ReportDatasource::getTenantId).eq(UserUtil.getCurrentUser().getTenantId())
                .and(ReportDatasource::getDelFlag).eq(ReportConstant.COMMON_YES_OR_NO_NO);
        if (commonPropertiesConfig.getEnabledTenant()) {
            queryWrapper.and(ReportDatasource::getTenantId).eq(UserUtil.getCurrentUser().getTenantId());
        }
        return this.mapper.selectListByQuery(queryWrapper);
    }

    /**
     * 执行SQL查询
     *
     * @param mesExecSqlDto 执行参数
     * @return 执行结果
     */
    @Override
    public List<Map<String, Object>> execSql(MesExecSqlDto mesExecSqlDto) throws SQLException, JsonProcessingException {
        ReportDatasource reportDatasource = this.getById(mesExecSqlDto.getDatasourceId());
        Assert.notNull(reportDatasource, "数据源不存在");
        switch (reportDatasource.getType()) {
            case ReportConstant.INFLUXDB ->{
                InfluxDBConnection influxDBConnection = new InfluxDBConnection(reportDatasource.getUserName(),
                        reportDatasource.getPassword(),
                        reportDatasource.getJdbcUrl().substring(0,reportDatasource.getJdbcUrl().lastIndexOf("/")),
                                reportDatasource.getJdbcUrl().substring(reportDatasource.getJdbcUrl().lastIndexOf("/")+1), null);
                return jdbcService.parseInfluxdbColumns(influxDBConnection, mesExecSqlDto.getTplSql(),
                        reportDatasource.getType(),mesExecSqlDto.getSqlParams());
            }
            case ReportConstant.ELASTIC_SEARCH_SQL,
                 ReportConstant.MYSQL,
                 ReportConstant.KUDU_IMAPLA,
                 ReportConstant.ORACLE,
                 ReportConstant.SQL_SERVER,
                 ReportConstant.JDBC,
                 ReportConstant.POSTGRESQL,
                 ReportConstant.DAMENG,
                 ReportConstant.OPENGAUSS,
                 ReportConstant.KINGBASE,
                 ReportConstant.TDENGINE -> {
                DataSourceConfig dataSourceConfig = new DataSourceConfig(reportDatasource.getId(), reportDatasource.getType(),
                        reportDatasource.getJdbcUrl(), reportDatasource.getUserName(),
                        reportDatasource.getPassword(), null);
                return jdbcService.parseMetaDataColumns(dataSourceConfig, mesExecSqlDto.getTplSql(),
                        reportDatasource.getType(),mesExecSqlDto.getSqlParams());
            }
        }
        return null;
    }

    /**
     * 获取下拉框数据
     *
     * @param param 参数
     * @return 数据
     */
    @Override
    public List<Map<String, Object>> getDatasourceSelectData(Map<String, String> param) throws SQLException {
        ReportDatasource reportDatasource = this.getById(param.get("dataSourceId"));
        String sqlText = param.get("selectContent");
        //数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig(reportDatasource.getId(),
                reportDatasource.getDriverClass(),
                reportDatasource.getJdbcUrl(),
                reportDatasource.getUserName(),
                reportDatasource.getPassword(), null);
        return jdbcService.getDatasourceDataBySql(dataSourceConfig, sqlText);
    }

    private boolean testHttp(ReportDatasource reportDatasource) throws JsonProcessingException {
        Map<String, String> headerMap = new HashMap<>();
        if(StringUtils.isNotEmpty(reportDatasource.getApiRequestHeader())){
            headerMap = JacksonUtil.jsonStringToMap(reportDatasource.getApiRequestHeader());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setAll(headerMap);
        HttpEntity<String> entity = new HttpEntity<>("", headers);
        ResponseEntity<Object> exchange = restTemplateOuter.exchange(reportDatasource.getJdbcUrl(),
                HttpMethod.valueOf(reportDatasource.getApiRequestType()),
                entity, Object.class);
        if (exchange.getStatusCode().isError()) {
            log.error("测试连接失败{}", Objects.requireNonNull(exchange.getBody()));
            return true;
        }
        return false;
    }
}
